package top.fangw.tenant.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.fangw.common.api.openfign.admin.*;
import top.fangw.common.api.vo.*;
import top.fangw.common.core.enums.TenantDataSourceTypeEnum;
import top.fangw.common.core.result.Result;
import top.fangw.common.flyway.util.CreateDatabase;
import top.fangw.common.flyway.util.CreateTable;
import top.fangw.common.flyway.util.UrlFormatUtil;
import top.fangw.common.mybatis.annotation.DyDataSource;
import top.fangw.common.mybatis.util.DynamicDataSourceUtil;
import top.fangw.tenant.mapper.*;
import top.fangw.tenant.pojo.dto.TenantInfoQueryDto;
import top.fangw.tenant.pojo.entity.*;
import top.fangw.tenant.service.IDatabaseInfoService;
import top.fangw.tenant.service.IDatasourceInfoService;
import top.fangw.tenant.service.ISourceDatabaseInfoService;
import top.fangw.tenant.service.ITenantInfoService;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 用途：用于管理系统中的租户信息 服务实现类
 * </p>
 *
 * @author fangw
 * @since 2024-01-16
 */
@Service
@RequiredArgsConstructor
public class TenantInfoServiceImpl extends ServiceImpl<TenantInfoMapper, TenantInfo> implements ITenantInfoService {

    private final TenantInfoMapper tenantInfoMapper;
    private final IDatasourceInfoService datasourceInfoService;
    private final IDatabaseInfoService databaseInfoService;
    private final ISourceDatabaseInfoService sourceDatabaseInfoService;
    private final DynamicDataSourceUtil dynamicDataSourceUtil;
    private final CreateTable createTable;
    private final RedisTemplate<Object, Object> redisTemplate;
    private final IUserServiceFeign userServiceFeign;
    private final IApplicationServiceFeign applicationServiceFeign;
    private final IMenuServiceFeign menuServiceFeign;
    private final PackageJonApplicationMapper packageJonApplicationMapper;
    private final PackageJonMenuMapper packageJonMenuMapper;
    private final ApplicationInfoMapper applicationInfoMapper;
    private final MenuInfoMapper menuInfoMapper;
    private final IRoleServiceFeign roleServiceFeign;
    private final IOrgInfoServiceFeign orgInfoServiceFeign;

    @Override
    @DyDataSource(self = true)
    @Transactional(rollbackFor = Exception.class)
    public void add(TenantInfo tenantInfo) {
        // 1、查询租户信息是否存在
        List<TenantInfo> tenantInfos = tenantInfoMapper.selectList(new LambdaQueryWrapper<TenantInfo>().eq(TenantInfo::getTenantName, tenantInfo.getTenantName()).or().eq(TenantInfo::getTenantId, tenantInfo.getTenantId()));
        if (tenantInfos.size() > 0) {
            throw new RuntimeException("租户信息已存在");
        }
        handlerDataSourceInfo(tenantInfo);

    }

    @Cacheable(value = "tenantInfo", key = "#tenantId", unless = "#result == null")
    @DyDataSource(self = true)
    @Override
    public TenantInfo getByTenantId(String tenantId) {
        TenantInfo res = tenantInfoMapper.selectOne(new LambdaQueryWrapper<TenantInfo>().eq(TenantInfo::getTenantId, tenantId));
        return res;
    }

    @Override
    public IPage<TenantInfo> pageList(TenantInfoQueryDto queryDto) {
        IPage<TenantInfo> page = new Page<>(queryDto.getCurrent(), queryDto.getSize());
        LambdaQueryWrapper<TenantInfo> queryWrapper = new LambdaQueryWrapper<TenantInfo>().orderByDesc(TenantInfo::getCreatedAt);
        if (StrUtil.isNotBlank(queryDto.getTenantName())) {
            queryWrapper.like(TenantInfo::getTenantName, queryDto.getTenantName());
        }
        if (StrUtil.isNotBlank(queryDto.getTenantId())) {
            queryWrapper.likeLeft(TenantInfo::getTenantId, queryDto.getTenantId());
        }
        if (StrUtil.isNotBlank(queryDto.getDatasourceType())) {
            queryWrapper.eq(TenantInfo::getDatasourceType, queryDto.getDatasourceType());
        }
        if (StrUtil.isNotBlank(queryDto.getTenantPackageId())) {
            queryWrapper.eq(TenantInfo::getTenantPackageId, queryDto.getTenantPackageId());
        }
        return this.baseMapper.selectPage(page, queryWrapper);
    }

    @GlobalTransactional(rollbackFor = Exception.class)
    @DyDataSource(self = true)
    public void handlerDataSourceInfo(TenantInfo tenantInfo) {
        // 对应分库策略的租户信息创建
        if (tenantInfo.getDatasourceType().equals(TenantDataSourceTypeEnum.DATABASE.getName())) {
            // 查询数据源信息
            DatasourceInfo datasourceInfo = datasourceInfoService.getById(tenantInfo.getDatasourceId());
            Assert.notNull(datasourceInfo, "数据源信息不存在");
            List<DatabaseInfo> databaseInfoList = databaseInfoService.list();
            Assert.notEmpty(databaseInfoList, "业务数据库信息不存在");
            // 遍历所有的业务数据库
            databaseInfoList.forEach(item -> {
                String dbName = item.getName() + "_" + tenantInfo.getTenantId();
                // 创建数据库
                CreateDatabase.init(datasourceInfo.getUrl(), datasourceInfo.getUsername(), datasourceInfo.getPassword(), dbName);
                // 创建租户数据源数据库信息
                SourceDatabaseInfo sourceDatabaseInfo = new SourceDatabaseInfo();
                sourceDatabaseInfo.setTenantId(tenantInfo.getTenantId())
                        .setDatabaseName(dbName)
                        .setDatasourceName(dbName)
                        .setDriverClassName(datasourceInfo.getDriverClassName())
                        .setUrl(String.format(UrlFormatUtil.formatTableUrl(datasourceInfo.getUrl()), dbName))
                        .setUsername(datasourceInfo.getUsername())
                        .setPassword(datasourceInfo.getPassword())
                        .setCreatedBy(tenantInfo.getCreatedBy())
                        .setCreatedByName(tenantInfo.getCreatedByName());
                sourceDatabaseInfo.setUpdatedBy(tenantInfo.getUpdatedBy())
                        .setUpdatedByName(tenantInfo.getUpdatedByName());
                sourceDatabaseInfoService.save(sourceDatabaseInfo);
                // 添加对应的动态数据源信息
                DataSourceProperty dataSourceProperty = BeanUtil.copyProperties(sourceDatabaseInfo, DataSourceProperty.class);
                dataSourceProperty.setPoolName(dbName);
                dynamicDataSourceUtil.addDataSource(dataSourceProperty);
                redisTemplate.opsForValue().set("my:dynamicDataSource" + dbName, dataSourceProperty);
                createTable.createTable(String.format(UrlFormatUtil.formatTableUrl(datasourceInfo.getUrl()), dbName), datasourceInfo.getUsername(), datasourceInfo.getPassword(), "1.0", tenantInfo.getTenantId(), Map.of(dbName, item.getName()), true);
            });
        } else if (tenantInfo.getDatasourceType().equals(TenantDataSourceTypeEnum.TABLE.getName())) {
            // 对应分表策略的租户信息添加
            DatasourceInfo datasourceInfo = datasourceInfoService.lambdaQuery().eq(DatasourceInfo::getDatasourceName, "base_info").one();
            Assert.notNull(datasourceInfo, "数据源信息不存在");
            List<DatabaseInfo> databaseInfoList = databaseInfoService.list();
            Assert.notEmpty(databaseInfoList, "业务数据库信息不存在");
            // 创建表并初始化数据
            databaseInfoList.forEach(item -> {
                String dbName = item.getName();
                // 更新flyway_schema_history
                // 连接数据库

                try {
                    Connection connection  = DriverManager.getConnection(String.format(UrlFormatUtil.formatTableUrl(datasourceInfo.getUrl()), dbName), datasourceInfo.getUsername(), datasourceInfo.getPassword());
                    // 创建Statement对象来执行SQL语句
                    Statement statement = connection.createStatement();
                    // 编写删除语句
                    String sql = "DELETE FROM flyway_schema_history";
                    // 执行删除语句
                    int affectedRows = statement.executeUpdate(sql);
                    // 输出结果
                    System.out.println("Deleted rows: " + affectedRows);
                    statement.close();
                    statement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }


                // 创建表并初始化数据
                createTable.createTable(String.format(UrlFormatUtil.formatTableUrl(datasourceInfo.getUrl()), dbName), datasourceInfo.getUsername(), datasourceInfo.getPassword(), "1.0", tenantInfo.getTenantId(), Map.of(dbName, item.getName()), true);
            });
        }
        // 4、添加租户信息
        tenantInfoMapper.insert(tenantInfo);
        TenantInfoVo tenantInfoVo = BeanUtil.copyProperties(tenantInfo, TenantInfoVo.class);
        redisTemplate.opsForValue().set("my:tenant:" + tenantInfo.getTenantId(), tenantInfoVo);

        // 初始化应用
        List<InitApplicationVo> initApplicationVoList = initApplication(tenantInfo);
        // 初始化菜单
        List<InitMenuVo> initMenuVos = initMenu(tenantInfo);
        // 初始化租户管理员
        InitAdminUserVo initAdminUserVo = initAdminUser(tenantInfo);
        // 初始化机构信息
        Assert.notNull(initAdminUserVo, "初始化管理员用户失败");
        InitOrgVo initOrgVo = initOrgInfo(tenantInfo, initAdminUserVo.getId());
        // 初始化角色信息
        Assert.notNull(initOrgVo, "初始化机构信息失败");
        InitRoleVo initRoleVo = initRoleInfo(tenantInfo, initAdminUserVo, initOrgVo, initApplicationVoList, initMenuVos);
        // 移除缓存的租户信息
        redisTemplate.delete("my:tenant:" + tenantInfo.getTenantId());

    }


    /**
     * 初始化应用信息
     *
     * @param tenantInfo 租户信息
     * @return 应用信息
     */

    private List<InitApplicationVo> initApplication(TenantInfo tenantInfo) {
        // 1、根据套餐信息初始化应用信息
        // 1.1 查询套餐下的应用信息
        List<PackageJonApplication> packageJonApplications = packageJonApplicationMapper.selectList(new LambdaQueryWrapper<PackageJonApplication>().eq(PackageJonApplication::getTenantPackageId, tenantInfo.getTenantPackageId()));
        List<String> applicationIds = packageJonApplications.stream().map(PackageJonApplication::getApplicationId).toList();
        List<ApplicationInfo> applicationInfos = new ArrayList<>();
        if (applicationIds.size() > 0) {
            applicationInfos = applicationInfoMapper.selectBatchIds(applicationIds);
        }
        List<InitApplicationVo> list = applicationInfos.stream().map(item -> {
            InitApplicationVo initAdminUserVo = BeanUtil.copyProperties(item, InitApplicationVo.class);
            initAdminUserVo.setTenantId(tenantInfo.getTenantId());
            return initAdminUserVo;
        }).toList();
        // 初始化租户应用信息
        applicationServiceFeign.initTenantApplication(new InitTenantApplication().setApplicationVoList(list).setTenantId(tenantInfo.getTenantId()));
        return list;
    }


    /**
     * 初始化菜单信息
     *
     * @param tenantInfo 租户信息
     * @return 初始化后的菜单信息
     */
    private List<InitMenuVo> initMenu(TenantInfo tenantInfo) {
        // 2、根据套餐信息初始化菜单信息
        // 2、1 查询套餐下的菜单信息
        List<PackageJonMenu> packageJonMenus = packageJonMenuMapper.selectList(new LambdaQueryWrapper<PackageJonMenu>().eq(PackageJonMenu::getTenantPackageId, tenantInfo.getTenantPackageId()));
        List<String> menuIdList = packageJonMenus.stream().map(PackageJonMenu::getMenuId).toList();
        List<MenuInfo> menuInfos = menuInfoMapper.selectBatchIds(menuIdList);
        List<InitMenuVo> menuVoList = menuInfos.stream().map(item -> {
            InitMenuVo initMenuVo = BeanUtil.copyProperties(item, InitMenuVo.class);
            initMenuVo.setTenantId(tenantInfo.getTenantId());
            return initMenuVo;
        }).toList();
        // 初始化租户菜单信息
        menuServiceFeign.initTenantMenu(new InitTenantMenu().setInitMenuVoList(menuVoList).setTenantId(tenantInfo.getTenantId()));
        return menuVoList;
    }

    /**
     * 初始化租户管理员信息
     *
     * @param tenantInfo 租户信息
     * @return 初始化后的管理员信息
     */
    private InitAdminUserVo initAdminUser(TenantInfo tenantInfo) {
        // 3、初始化租户管理员用户信息
        InitAdminUserVo initAdminUserVo = new InitAdminUserVo().buildAdminUser(tenantInfo.getContact(), tenantInfo.getTenantId(), tenantInfo.getTelephone());
        Result<InitAdminUserVo> initAdminUserVoResult = userServiceFeign.initTenantAdminUser(initAdminUserVo);
        if (initAdminUserVoResult.getStatus()) {
            initAdminUserVo = initAdminUserVoResult.getData();
            return initAdminUserVo;
        }
        return null;
    }

    /**
     * 初始化机构信息
     *
     * @param tenantInfo 租户信息
     * @param master     机构负责人信息
     * @return 初始化后的机构信息
     */
    private InitOrgVo initOrgInfo(TenantInfo tenantInfo, String master) {
        // 4、初始化租户机构信息
        InitOrgVo initOrgVo = new InitOrgVo().buildDefault(tenantInfo.getTenantId(), tenantInfo.getCompanyName(), tenantInfo.getTelephone(), master, tenantInfo.getContact());
        Result<InitOrgVo> initOrgVoResult = orgInfoServiceFeign.initTenantOrg(initOrgVo);
        if (initOrgVoResult.getStatus()) {
            initOrgVo = initOrgVoResult.getData();
            return initOrgVo;
        }
        return null;
    }

    /**
     * 初始化角色信息
     *
     * @param tenantInfo            租户信息
     * @param initAdminUserVo       管理员信息
     * @param initOrgVo             机构信息
     * @param initApplicationVoList 应用信息
     * @param initMenuVos           菜单信息
     * @return 初始化后的角色信息
     */
    private InitRoleVo initRoleInfo(TenantInfo tenantInfo, InitAdminUserVo initAdminUserVo, InitOrgVo initOrgVo, List<InitApplicationVo> initApplicationVoList, List<InitMenuVo> initMenuVos) {
        InitRoleVo initRoleVo = new InitRoleVo().buildDefaultMenu(tenantInfo.getTenantId(), initOrgVo.getId());
        initRoleVo.setAdminUserId(initAdminUserVo.getId());
        initRoleVo.setInitApplicationVoList(initApplicationVoList);
        initRoleVo.setInitRoleJoinMenuList(initMenuVos);
        // 5、初始化租户角色信息
        Result<InitRoleVo> initRoleVoResult = roleServiceFeign.initTenantRole(initRoleVo);
        if (initRoleVoResult.getStatus()) {
            initRoleVo = initRoleVoResult.getData();
            return initRoleVo;
        }
        return null;
    }


}
